From 5e8bc03f674a082db4bdf1c6f8b16b4ba9bf4598 Mon Sep 17 00:00:00 2001 From: "kaf24@firebug.cl.cam.ac.uk" Date: Wed, 27 Jul 2005 15:32:44 +0000 Subject: [PATCH] Add HPET support to Xen. It is quicker to access and more precise than the PIT. Signed-off-by: Keir Fraser --- xen/arch/x86/acpi/boot.c | 2 +- xen/arch/x86/time.c | 152 ++++++++++++++++++++++++++++++++--- xen/include/asm-x86/config.h | 1 + xen/include/asm-x86/fixmap.h | 1 + xen/include/asm-x86/hpet.h | 52 ++++++++++++ 5 files changed, 198 insertions(+), 10 deletions(-) create mode 100644 xen/include/asm-x86/hpet.h diff --git a/xen/arch/x86/acpi/boot.c b/xen/arch/x86/acpi/boot.c index c209012950..2d5b82cda4 100644 --- a/xen/arch/x86/acpi/boot.c +++ b/xen/arch/x86/acpi/boot.c @@ -382,7 +382,7 @@ static int __init acpi_parse_hpet(unsigned long phys, unsigned long size) return -1; } -#ifdef CONFIG_X86_64 +#if 0/*def CONFIG_X86_64*/ vxtime.hpet_address = hpet_tbl->addr.addrl | ((long) hpet_tbl->addr.addrh << 32); diff --git a/xen/arch/x86/time.c b/xen/arch/x86/time.c index 74de8a7ef7..e9eda9f99b 100644 --- a/xen/arch/x86/time.c +++ b/xen/arch/x86/time.c @@ -28,11 +28,18 @@ #include #include #include +#include #include +/* opt_hpet_force: If true, force HPET configuration via PCI space. */ +/* NB. This is a gross hack. Mainly useful for HPET testing. */ +static int opt_hpet_force = 0; +boolean_param("hpet_force", opt_hpet_force); + #define EPOCH MILLISECS(1000) unsigned long cpu_khz; /* CPU clock frequency in kHz. */ +unsigned long hpet_address; spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; int timer_ack = 0; unsigned long volatile jiffies; @@ -58,6 +65,7 @@ static s_time_t stime_platform_stamp; static u64 platform_timer_stamp; static struct time_scale platform_timer_scale; static spinlock_t platform_timer_lock = SPIN_LOCK_UNLOCKED; +static u64 (*read_platform_count)(void); static inline u32 down_shift(u64 time, int shift) { @@ -225,8 +233,13 @@ void calibrate_tsc_ap(void) atomic_dec(&tsc_calibrate_gang); } + +/************************************************************ + * PLATFORM TIMER 1: PROGRAMMABLE INTERVAL TIMER (LEGACY PIT) + */ + /* Protected by platform_timer_lock. */ -static u64 platform_pit_counter; +static u64 pit_counter64; static u16 pit_stamp; static struct ac_timer pit_overflow_timer; @@ -246,21 +259,137 @@ static void pit_overflow(void *unused) spin_lock(&platform_timer_lock); counter = pit_read_counter(); - platform_pit_counter += (u16)(pit_stamp - counter); + pit_counter64 += (u16)(pit_stamp - counter); pit_stamp = counter; spin_unlock(&platform_timer_lock); set_ac_timer(&pit_overflow_timer, NOW() + MILLISECS(20)); } -static void init_platform_timer(void) +static u64 read_pit_count(void) +{ + return pit_counter64 + (u16)(pit_stamp - pit_read_counter()); +} + +static int init_pit(void) { + read_platform_count = read_pit_count; + init_ac_timer(&pit_overflow_timer, pit_overflow, NULL, 0); pit_overflow(NULL); - platform_timer_stamp = platform_pit_counter; + platform_timer_stamp = pit_counter64; set_time_scale(&platform_timer_scale, CLOCK_TICK_RATE); + + return 1; +} + +/************************************************************ + * PLATFORM TIMER 2: HIGH PRECISION EVENT TIMER (HPET) + */ + +/* Protected by platform_timer_lock. */ +static u64 hpet_counter64, hpet_overflow_period; +static u32 hpet_stamp; +static struct ac_timer hpet_overflow_timer; + +static void hpet_overflow(void *unused) +{ + u32 counter; + + spin_lock(&platform_timer_lock); + counter = hpet_read32(HPET_COUNTER); + hpet_counter64 += (u32)(counter - hpet_stamp); + hpet_stamp = counter; + spin_unlock(&platform_timer_lock); + + set_ac_timer(&hpet_overflow_timer, NOW() + hpet_overflow_period); +} + +static u64 read_hpet_count(void) +{ + return hpet_counter64 + (u32)(hpet_read32(HPET_COUNTER) - hpet_stamp); +} + +static int init_hpet(void) +{ + u64 hpet_rate; + u32 hpet_id, hpet_period, cfg; + int i; + + if ( (hpet_address == 0) && opt_hpet_force ) + { + printk(KERN_WARNING "WARNING: Enabling HPET base manually!\n"); + outl(0x800038a0, 0xcf8); + outl(0xff000001, 0xcfc); + outl(0x800038a0, 0xcf8); + hpet_address = inl(0xcfc) & 0xfffffffe; + printk(KERN_WARNING "WARNING: Enabled HPET at %#lx.\n", hpet_address); + } + + if ( hpet_address == 0 ) + return 0; + + set_fixmap_nocache(FIX_HPET_BASE, hpet_address); + + hpet_id = hpet_read32(HPET_ID); + if ( hpet_id == 0 ) + { + printk("BAD HPET vendor id.\n"); + return 0; + } + + /* Check for sane period (100ps <= period <= 100ns). */ + hpet_period = hpet_read32(HPET_PERIOD); + if ( (hpet_period > 100000000) || (hpet_period < 100000) ) + { + printk("BAD HPET period %u.\n", hpet_period); + return 0; + } + + cfg = hpet_read32(HPET_CFG); + cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY); + hpet_write32(cfg, HPET_CFG); + + for ( i = 0; i <= ((hpet_id >> 8) & 31); i++ ) + { + cfg = hpet_read32(HPET_T0_CFG + i*0x20); + cfg &= ~HPET_TN_ENABLE; + hpet_write32(cfg & ~HPET_TN_ENABLE, HPET_T0_CFG); + } + + cfg = hpet_read32(HPET_CFG); + cfg |= HPET_CFG_ENABLE; + hpet_write32(cfg, HPET_CFG); + + read_platform_count = read_hpet_count; + + hpet_rate = 1000000000000000ULL; /* 10^15 */ + (void)do_div(hpet_rate, hpet_period); + set_time_scale(&platform_timer_scale, hpet_rate); + + /* Trigger overflow avoidance roughly when counter increments 2^31. */ + if ( (hpet_rate >> 31) != 0 ) + { + hpet_overflow_period = MILLISECS(1000); + (void)do_div(hpet_overflow_period, (u32)(hpet_rate >> 31) + 1); + } + else + { + hpet_overflow_period = MILLISECS(1000) << 31; + (void)do_div(hpet_overflow_period, (u32)hpet_rate); + } + + init_ac_timer(&hpet_overflow_timer, hpet_overflow, NULL, 0); + hpet_overflow(NULL); + platform_timer_stamp = hpet_counter64; + + return 1; } +/************************************************************ + * GENERIC PLATFORM TIMER INFRASTRUCTURE + */ + static s_time_t __read_platform_stime(u64 platform_time) { u64 diff64 = platform_time - platform_timer_stamp; @@ -276,7 +405,7 @@ static s_time_t read_platform_stime(void) s_time_t stime; spin_lock(&platform_timer_lock); - counter = platform_pit_counter + (u16)(pit_stamp - pit_read_counter()); + counter = read_platform_count(); stime = __read_platform_stime(counter); spin_unlock(&platform_timer_lock); @@ -289,13 +418,19 @@ static void platform_time_calibration(void) s_time_t stamp; spin_lock(&platform_timer_lock); - counter = platform_pit_counter + (u16)(pit_stamp - pit_read_counter()); + counter = read_platform_count(); stamp = __read_platform_stime(counter); stime_platform_stamp = stamp; platform_timer_stamp = counter; spin_unlock(&platform_timer_lock); } +static void init_platform_timer(void) +{ + if ( !init_hpet() ) + BUG_ON(!init_pit()); +} + /*************************************************************************** * CMOS Timer functions @@ -494,9 +629,8 @@ static void local_time_calibration(void *unused) #if 0 printk("PRE%d: tsc=%lld stime=%lld master=%lld\n", cpu, prev_tsc, prev_local_stime, prev_master_stime); - printk("CUR%d: tsc=%lld stime=%lld master=%lld %lld\n", - cpu, curr_tsc, curr_local_stime, curr_master_stime, - platform_pit_counter); + printk("CUR%d: tsc=%lld stime=%lld master=%lld\n", + cpu, curr_tsc, curr_local_stime, curr_master_stime); #endif /* Local time warps forward if it lags behind master time. */ diff --git a/xen/include/asm-x86/config.h b/xen/include/asm-x86/config.h index 506515c683..d24759aacb 100644 --- a/xen/include/asm-x86/config.h +++ b/xen/include/asm-x86/config.h @@ -23,6 +23,7 @@ #define CONFIG_X86_LOCAL_APIC 1 #define CONFIG_X86_GOOD_APIC 1 #define CONFIG_X86_IO_APIC 1 +#define CONFIG_HPET_TIMER 1 /* Intel P4 currently has largest cache line (L2 line size is 128 bytes). */ #define CONFIG_X86_L1_CACHE_SHIFT 7 diff --git a/xen/include/asm-x86/fixmap.h b/xen/include/asm-x86/fixmap.h index ca01c687fe..586128baf8 100644 --- a/xen/include/asm-x86/fixmap.h +++ b/xen/include/asm-x86/fixmap.h @@ -30,6 +30,7 @@ enum fixed_addresses { FIX_IO_APIC_BASE_END = FIX_IO_APIC_BASE_0 + MAX_IO_APICS-1, FIX_ACPI_BEGIN, FIX_ACPI_END = FIX_ACPI_BEGIN + FIX_ACPI_PAGES - 1, + FIX_HPET_BASE, __end_of_fixed_addresses }; diff --git a/xen/include/asm-x86/hpet.h b/xen/include/asm-x86/hpet.h new file mode 100644 index 0000000000..f3c27cb6a6 --- /dev/null +++ b/xen/include/asm-x86/hpet.h @@ -0,0 +1,52 @@ +#ifndef __X86_HPET_H__ +#define __X86_HPET_H__ + +/* + * Documentation on HPET can be found at: + * http://www.intel.com/ial/home/sp/pcmmspec.htm + * ftp://download.intel.com/ial/home/sp/mmts098.pdf + */ + +#define HPET_MMAP_SIZE 1024 + +#define HPET_ID 0x000 +#define HPET_PERIOD 0x004 +#define HPET_CFG 0x010 +#define HPET_STATUS 0x020 +#define HPET_COUNTER 0x0f0 +#define HPET_T0_CFG 0x100 +#define HPET_T0_CMP 0x108 +#define HPET_T0_ROUTE 0x110 +#define HPET_T1_CFG 0x120 +#define HPET_T1_CMP 0x128 +#define HPET_T1_ROUTE 0x130 +#define HPET_T2_CFG 0x140 +#define HPET_T2_CMP 0x148 +#define HPET_T2_ROUTE 0x150 + +#define HPET_ID_VENDOR 0xffff0000 +#define HPET_ID_LEGSUP 0x00008000 +#define HPET_ID_NUMBER 0x00001f00 +#define HPET_ID_REV 0x000000ff +#define HPET_ID_NUMBER_SHIFT 8 + +#define HPET_ID_VENDOR_SHIFT 16 +#define HPET_ID_VENDOR_8086 0x8086 + +#define HPET_CFG_ENABLE 0x001 +#define HPET_CFG_LEGACY 0x002 +#define HPET_LEGACY_8254 2 +#define HPET_LEGACY_RTC 8 + +#define HPET_TN_ENABLE 0x004 +#define HPET_TN_PERIODIC 0x008 +#define HPET_TN_PERIODIC_CAP 0x010 +#define HPET_TN_SETVAL 0x040 +#define HPET_TN_32BIT 0x100 + +#define hpet_read32(x) \ + (*(volatile u32 *)(fix_to_virt(FIX_HPET_BASE) + (x))) +#define hpet_write32(y,x) \ + (*(volatile u32 *)(fix_to_virt(FIX_HPET_BASE) + (x)) = (y)) + +#endif /* __X86_HPET_H__ */ -- 2.30.2